home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / aplictns / graph.1 < prev    next >
Internet Message Format  |  1989-11-13  |  29KB

  1. Path: xanth!ukma!tut.cis.ohio-state.edu!gem.mps.ohio-state.edu!wuarchive!texbell!texsun!newstop!sun!swap!page
  2. From: page%swap@Sun.COM (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i204:  graph - plot mathematical functions, Part01/07
  5. Message-ID: <127782@sun.Eng.Sun.COM>
  6. Date: 13 Nov 89 02:31:49 GMT
  7. Sender: news@sun.Eng.Sun.COM
  8. Lines: 881
  9. Approved: page@sun.com
  10.  
  11. Submitted-by: dg3i+@andrew.cmu.edu (David Gay)
  12. Posting-number: Volume 89, Issue 204
  13. Archive-name: applications/graph.1
  14.  
  15. This program draws mathematical functions on a plane.
  16. Text & axes may be added, and the result saved to disk or printed.
  17.  
  18.   To compile it, you'll need:
  19.    - Lattice C V5.02
  20.    - My eval library (which, if I'm not mistaken, and for obscure reasons,
  21.      came over comp.binaries.amiga a few months ago. It's also on fish
  22.      disk 192).
  23.    - The ARP include files, with a few changes to libraries/arp_pragmas.h:
  24.      Add a #ifndef NODOS/#endif around the #pragmas Open to Execute.
  25.    - 1 Meg of memory ...
  26.  
  27. # This is a shell archive.
  28. # Remove anything above and including the cut line.
  29. # Then run the rest of the file through 'sh'.
  30. # Unpacked files will be owned by you and have default permissions.
  31. #----cut here-----cut here-----cut here-----cut here----#
  32. #!/bin/sh
  33. # shar: SHell ARchive
  34. # Run the following text through 'sh' to create:
  35. #    README
  36. #    coords.c
  37. #    coords.h
  38. #    default.c
  39. #    default.h
  40. #    f_of_x.c
  41. # This is archive 1 of a 7-part kit.
  42. # This archive created: Sun Nov 12 18:23:29 1989
  43. echo "extracting README"
  44. sed 's/^X//' << \SHAR_EOF > README
  45. X    This program draws mathematical functions on a plane. Text & axes may
  46. X    be added, and the result saved to disk or printed.
  47. X
  48. X    To compile it, you'll need:
  49. X
  50. X     - Lattice C V5.02
  51. X     - My eval library (which, if I'm not mistaken, and for obscure reasons,
  52. X       came over comp.binaries.amiga a few months ago. It's also on fish
  53. X       disk 192).
  54. X     - The ARP include files, with a few changes to libraries/arp_pragmas.h:
  55. X       Add a #ifndef NODOS/#endif around the #pragmas Open to Execute.
  56. X     - 1 Meg of memory ...
  57. X
  58. X    To install the source correctly, create a directory graph, unshar all
  59. X    this in it, and Execute mv2dir. On a floppy system, format a new disk
  60. X    and unshar onto that ... (You'll have to change a few things because
  61. X    the debuggable objects & executable won't fit on one disk).
  62. X
  63. X    To compile, the file eval.h (from the eval library) should be accessible
  64. X    with a '#include "user/eval.h"'. Now, simply type
  65. X
  66. X      lmk -f lmkO ; for an optimised version
  67. X
  68. X      lmk ; for a debuggable version
  69. X
  70. X    The program is commented, though not extensively. If you want to add any
  71. X    objects, read the file object.guidelines first.
  72. X
  73. X
  74. X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  75. XDavid Gay
  76. X  "(p.suiv :=: q.prec.suiv).prec :=: q.prec"
  77. X  You don't want to know about this language !
  78. X
  79. XGAY_D@ELMA.EPFL.CH, or GAY_D%ELMA.EPFL.CH@CLSEPF51.bitnet
  80. X(Till mid-august 89)
  81. X
  82. X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  83. X
  84. SHAR_EOF
  85. echo "extracting coords.c"
  86. sed 's/^X//' << \SHAR_EOF > coords.c
  87. X/*
  88. X *                 GRAPH, Version 1.00 - 4 August 1989
  89. X *
  90. X *            Copyright 1989, David Gay. All Rights Reserved.
  91. X *            This software is freely redistrubatable.
  92. X */
  93. X
  94. X/* Set up a coordinate system in a Rastport */
  95. X/* Copyright 1989, David Gay */
  96. X#include <exec/types.h>
  97. X#include <graphics/regions.h>
  98. X#include <intuition/intuition.h>
  99. X#include <proto/graphics.h>
  100. X#include <proto/layers.h>
  101. X
  102. X#include <limits.h>
  103. X#include <math.h>
  104. X
  105. X#include "coords.h"
  106. X#include "tracker.h"
  107. X
  108. Xextern int _FPERR;
  109. X
  110. X/* Actual structure used */
  111. Xstruct RWin {
  112. X    struct RWindow rw;
  113. X    double xmin, xscale, ymin, yscale;
  114. X    double xoffset, yoffset;
  115. X    struct Region *clip, *oldRegion;
  116. X    int lostpos;
  117. X};
  118. X
  119. X/*-------------------------------------------------------------------------*/
  120. X/*                            RWindow definition                           */
  121. X/*-------------------------------------------------------------------------*/
  122. X
  123. X/* convert double to integer, round down. cf floor */
  124. Xlong ftol(double x)
  125. X{
  126. X    if (x >= 0) return (long)x;
  127. X    else return (long)floor(x);
  128. X}
  129. X
  130. X/* Do Move/Draw style operation func, checking for overflow, etc */
  131. Xstatic void rdo_RWin(struct RWin *this, void (*func)(struct RastPort *rp, long
  132. Xsx, long sy), double x, double y)
  133. X{
  134. X    double sx, sy;
  135. X
  136. X    if (this->lostpos) func = Move;
  137. X
  138. X    _FPERR = 0;
  139. X    sx = this->rw.sx((struct RWindow *)this, x);
  140. X    sy = this->rw.sy((struct RWIndow *)this, y);
  141. X    /* Move, Draw only accept shorts */
  142. X    this->lostpos = (_FPERR != 0 || fabs(sx) > SHRT_MAX || fabs(sy) > SHRT_MAX)
  143. X;
  144. X
  145. X    if (!this->lostpos) func(this->rw.rp, ftol(sx), ftol(sy));
  146. X}
  147. X
  148. X/* The various conversion routines, to/from ints, for lin orr log scales, x or
  149. Xy ax */
  150. Xstatic double sx_lin(struct RWin *this, double x)
  151. X{
  152. X    return (x - this->xmin) * this->xscale + this->xoffset;
  153. X}
  154. X
  155. Xstatic double sx_log(struct RWin *this, double x)
  156. X{
  157. X    return (log10(x) - this->xmin) * this->xscale + this->xoffset;
  158. X}
  159. X
  160. Xstatic double sy_lin(struct RWin *this, double y)
  161. X{
  162. X    return (y - this->ymin) * this->yscale + this->yoffset;
  163. X}
  164. X
  165. Xstatic double sy_log(struct RWin *this, double y)
  166. X{
  167. X    return (log10(y) - this->ymin) * this->yscale + this->yoffset;
  168. X}
  169. X
  170. Xstatic double x_lin(struct RWin *this, long sx)
  171. X{
  172. X    return (sx - this->xoffset) / this->xscale + this->xmin;
  173. X}
  174. X
  175. Xstatic double x_log(struct RWin *this, long sx)
  176. X{
  177. X    return pow(10.0, (sx - this->xoffset) / this->xscale + this->xmin);
  178. X}
  179. X
  180. Xstatic double y_lin(struct RWin *this, long sy)
  181. X{
  182. X    return (sy - this->yoffset) / this->yscale + this->ymin;
  183. X}
  184. X
  185. Xstatic double y_log(struct RWin *this, long sy)
  186. X{
  187. X    return pow(10.0, (sy - this->yoffset) / this->yscale + this->ymin);
  188. X}
  189. X
  190. X/* Delete a member of this class */
  191. Xstatic void delete_RWin(struct RWin *this)
  192. X{
  193. X    if (this->clip)
  194. X    {
  195. X        InstallClipRegion(this->rw.rp->Layer, this->oldRegion);
  196. X        DisposeRegion(this->clip);
  197. X    }
  198. X    FreeMem(this, sizeof(struct RWin));
  199. X}
  200. X
  201. X/* Create a coordinate system in Rastport rp (w by h pixels),
  202. X   {...}offset : offset in rp at which coords starts (normally > 0)
  203. X   {x,y}{min,max} : limits for coords
  204. X   logx, logy : logarithmic scale ?
  205. X   clip : setup clipping to {...}offset boundaries ?
  206. X*/
  207. Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h,
  208. X                            long leftoffset, long bottomoffset, long rightoffse
  209. Xt, long topoffset,
  210. X                            double xmin, double ymin, double xmax, double ymax,
  211. X     
  212. X                            long logx, long logy, long clip)
  213. X{
  214. X    long width, height;
  215. X    struct Rectangle rect;
  216. X    struct Region *r;
  217. X    struct RWin *this = AllocMem(sizeof(struct RWin), 0L);
  218. X
  219. X    if (this)
  220. X    {
  221. X        /* Setup class methods, and private data */
  222. X        this->rw.delete = (void *)delete_RWin;
  223. X        this->rw.rdo = (void *)rdo_RWin;
  224. X        /* Setup scaling */
  225. X        this->rw.rp = rp;
  226. X        this->xoffset = leftoffset;
  227. X        this->yoffset = h - bottomoffset - 1;
  228. X        width = w - leftoffset - rightoffset - 1;
  229. X        height = h - bottomoffset - topoffset - 1;
  230. X        if (logx)
  231. X        {
  232. X            this->xmin = log10(xmin);
  233. X            this->xscale = width / (log10(xmax) - this->xmin);
  234. X            this->rw.sx = (void *)sx_log;
  235. X            this->rw.x = (void *)x_log;
  236. X        }
  237. X        else
  238. X        {
  239. X            this->xmin = xmin;
  240. X            this->xscale = width / (xmax - this->xmin);
  241. X            this->rw.sx = (void *)sx_lin;
  242. X            this->rw.x = (void *)x_lin;
  243. X        }
  244. X        if (logy)
  245. X        {
  246. X            this->ymin = log10(ymin);
  247. X            this->yscale = height / (this->ymin - log10(ymax));
  248. X            this->rw.sy = (void *)sy_log;
  249. X            this->rw.y = (void *)y_log;
  250. X        }
  251. X        else
  252. X        {
  253. X            this->ymin = ymin;
  254. X            this->yscale = height / (this->ymin - ymax);
  255. X            this->rw.sy = (void *)sy_lin;
  256. X            this->rw.y = (void *)y_lin;
  257. X        }
  258. X
  259. X        if (clip)
  260. X        {
  261. X            /* Setup clipping */
  262. X            if (r = NewRegion())
  263. X            {
  264. X                rect.MinX = leftoffset;
  265. X                rect.MaxX = w - rightoffset - 1;
  266. X                rect.MinY = topoffset;
  267. X                rect.MaxY = h - bottomoffset - 1;
  268. X                if (OrRectRegion(r, &rect))
  269. X                {
  270. X                    this->clip = r;
  271. X
  272. X/* Remark: Due to a bug(?) in InstallClipRegion, make sure that the currently
  273. X  installed region when EndRefresh is called is not NULL (trashed windows
  274. X  otherwise...). */
  275. X                    this->oldRegion = InstallClipRegion(rp->Layer, r);
  276. X                }
  277. X                else
  278. X                {
  279. X                    DisposeRegion(r);
  280. X                    r = NULL;
  281. X                }
  282. X            }
  283. X            if (!r)
  284. X            {
  285. X                FreeMem(this, sizeof(struct RWin));
  286. X                this = NULL;
  287. X            }
  288. X        }
  289. X        else
  290. X            this->clip = NULL;
  291. X    }
  292. X    /* Return the newly allocated instance */
  293. X    return (struct RWindow *)this;
  294. X}
  295. X
  296. SHAR_EOF
  297. echo "extracting coords.h"
  298. sed 's/^X//' << \SHAR_EOF > coords.h
  299. X/*
  300. X *                 GRAPH, Version 1.00 - 4 August 1989
  301. X *
  302. X *            Copyright 1989, David Gay. All Rights Reserved.
  303. X *            This software is freely redistrubatable.
  304. X */
  305. X
  306. X/* Set up a coordinate system in a Rastport */
  307. X#ifndef COORDS_H
  308. X#define COORDS_H
  309. X
  310. X/* The class used */
  311. Xstruct RWindow {
  312. X    struct RastPort *rp; /* The rastport associated woth this coord system */
  313. X    /* The methods : */
  314. X    void (*delete)(struct RWindow *this);
  315. X    /* Do a Movw/Draw, checks for overflow, etc */
  316. X    void (*rdo)(struct RWindow *this, void (*func)(struct RastPort *rp, long sx
  317. X, long sy), double x, double y);
  318. X    double (*sx)(struct RWindow *this, double x); /* real coords -> rastport co
  319. Xords */
  320. X    double (*sy)(struct RWindow *this, double y);
  321. X    double (*x)(struct RWindow *this, long sx);   /* rastport coords -> real co
  322. Xords */
  323. X    double (*y)(struct RWindow *this, long sy);
  324. X};
  325. X
  326. X/* Create a coordinate system in Rastport rp (w by h pixels),
  327. X   {x,y}{min,max}offset : offset in rp at which coords starts (normally > 0)
  328. X   {x,y}{min,max} : limits for coords
  329. X   logx, logy : logarithmic scale ?
  330. X   clip : setup clipping to {x,y}{min,max}offset boundaries ?
  331. X*/
  332. Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h,
  333. X                            long xminoffset, long yminoffset, long xmaxoffset,
  334. Xlong ymaxoffset,
  335. X                            double xmin, double ymin, double xmax, double ymax,
  336. X     
  337. X                            long logx, long logy, long clip);
  338. Xlong ftol(double x); /* convert double to integer, round down. cf floor */
  339. X
  340. Xextern void Move(), BigDraw();
  341. X
  342. X/* Easy calling for Move, Draw in real coords */
  343. X#define RMove(rwin, x, y) ((rwin)->rdo((rwin), Move, (x), (y)))
  344. X#define RDraw(rwin, x, y) ((rwin)->rdo((rwin), BigDraw, (x), (y)))
  345. X
  346. X#endif
  347. X
  348. SHAR_EOF
  349. echo "extracting default.c"
  350. sed 's/^X//' << \SHAR_EOF > default.c
  351. X/*
  352. X *                 GRAPH, Version 1.00 - 4 August 1989
  353. X *
  354. X *            Copyright 1989, David Gay. All Rights Reserved.
  355. X *            This software is freely redistrubatable.
  356. X */
  357. X
  358. X/* Default object methods */
  359. X
  360. X#include "object.h"
  361. X#include "object/default.h"
  362. X#include "uio.h"
  363. X
  364. Xint notdone(struct object *this)
  365. X{
  366. X    message(this->g, "Routine unimplemented", (char *)NULL);
  367. X
  368. X    return FALSE;
  369. X}
  370. X
  371. Xstruct Region *ref_uncalled(struct object *this)
  372. X{
  373. X    message(this->g, "Hurrah! You've found a bug!", (char *)NULL);
  374. X
  375. X    return NULL;
  376. X}
  377. X
  378. Xint uncalled(struct object *this)
  379. X{
  380. X    message(this->g, "Hurrah! You've found a bug!", (char *)NULL);
  381. X
  382. X    return FALSE;
  383. X}
  384. X
  385. SHAR_EOF
  386. echo "extracting default.h"
  387. sed 's/^X//' << \SHAR_EOF > default.h
  388. X/*
  389. X *                 GRAPH, Version 1.00 - 4 August 1989
  390. X *
  391. X *            Copyright 1989, David Gay. All Rights Reserved.
  392. X *            This software is freely redistrubatable.
  393. X */
  394. X
  395. X/* Default object methods */
  396. X#ifndef DEFAULT_H
  397. X#define DEFAULT_H
  398. X
  399. Xint uncalled(struct object *this);
  400. Xstruct Region *ref_uncalled(struct object *this);
  401. Xint notdone(struct object *this);
  402. X
  403. X#endif
  404. X
  405. SHAR_EOF
  406. echo "extracting f_of_x.c"
  407. sed 's/^X//' << \SHAR_EOF > f_of_x.c
  408. X/*
  409. X *                 GRAPH, Version 1.00 - 4 August 1989
  410. X *
  411. X *            Copyright 1989, David Gay. All Rights Reserved.
  412. X *            This software is freely redistrubatable.
  413. X */
  414. X
  415. X#include <exec/types.h>
  416. X#include <intuition/intuition.h>
  417. X#include <graphics/text.h>
  418. X#include <math.h>
  419. X#include <string.h>
  420. X
  421. X#include "object.h"
  422. X#include "object/function.h"
  423. X#include "object/default.h"
  424. X#include "file.h"
  425. X#include "graph.h"
  426. X#include "uio.h"
  427. X#include "coords.h"
  428. X#include "list.h"
  429. X#include "grph.h"
  430. X#include "user/eval.h"
  431. X#include "user/gadgets.h"
  432. X#include "tracker.h"
  433. X
  434. X#include <proto/exec.h>
  435. X#include <proto/intuition.h>
  436. X#include <proto/graphics.h>
  437. X
  438. X/* (private) class f_of_x, inherited from function */
  439. Xstruct f_of_x {
  440. X    struct function f;
  441. X    char expr[EXPRLEN];       /* the function */
  442. X    double oldxmin, oldxmax;  /* limits used at last calculation */
  443. X    int waslog;               /* previous x axis type */
  444. X    value function, derivee;  /* the compiled function & its differential */
  445. X};
  446. X
  447. X/*-------------------------------------------------------------------------*/
  448. X/*                      f_of_x class implementation                        */
  449. X/*-------------------------------------------------------------------------*/
  450. X
  451. X/* Return TRUE if f is displayable */
  452. Xstatic int f_of_x_ok(const struct f_of_x *this)
  453. X{
  454. X    return (this->f.min == NOVAL || this->f.max == NOVAL || this->f.min < this-
  455. X>f.max) &&
  456. X           (this->f.steps == INOVAL || this->f.steps >= 3);
  457. X}
  458. X
  459. X/* free resources used by this */
  460. Xstatic void destroy_f_of_x(struct f_of_x *this)
  461. X{
  462. X    free_var_list(&this->f.used);
  463. X    if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
  464. X    this->f.calc = FALSE;
  465. X    if (this->function) free_expr(this->function);
  466. X    if (this->derivee) free_expr(this->derivee);
  467. X    this->function = this->derivee = NULL;
  468. X}
  469. X
  470. X/* Initialise dependent parts of f_of_x */
  471. Xstatic int create_f_of_x(struct f_of_x *this)
  472. X{
  473. X    this->f.calc = FALSE;
  474. X    this->f.var.name = this->f.vname;
  475. X    this->function = compile(this->expr);
  476. X    if (eval_error != 0)
  477. X    {
  478. X        message(this->f.o.g, "Compilation error:", eval_messages[eval_error], (
  479. Xchar *)NULL);
  480. X        return FALSE;
  481. X    }
  482. X    this->derivee = differentiate(this->function, this->f.vname);
  483. X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
  484. X    {
  485. X        message(this->f.o.g, "Differentiation error:", eval_messages[eval_error
  486. X], (char *)NULL);
  487. X        return FALSE;
  488. X    }
  489. X    if (!make_var_list(this->function, &this->f.used))
  490. X        init_var_list(&this->f.used);
  491. X    return TRUE;
  492. X}
  493. X
  494. X/* Allow the user to edit this function (ref: area to refresh) */
  495. Xstatic int edit_f_of_x(struct f_of_x *this, struct Region **ref)
  496. X{
  497. X    struct Requester *req;
  498. X    struct Memory *m;
  499. X    struct Gadget *gl = NULL, *sd, *nd;
  500. X    char from[NBLEN], to[NBLEN], steps[INTLEN], expr[EXPRLEN], xname[VARLEN], c
  501. Xolour[INTLEN];
  502. X    int ret = FALSE;
  503. X
  504. X    /* Create requester */
  505. X    double2str(from, this->f.min);
  506. X    double2str(to, this->f.max);
  507. X    int2str(steps, this->f.steps);
  508. X    int2str(colour, this->f.colour);
  509. X    strcpy(expr, this->expr);
  510. X    strcpy(xname, this->f.vname);
  511. X
  512. X    *ref = NULL;
  513. X    if ((m = NewMemory()) &&
  514. X        (req = InitReq(50, 20, 255, 145, m)) &&
  515. X        SetReqBorder(req, 1, m) &&
  516. X        AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
  517. X        AddText(&gl, 0, "f(", FALSE, xname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
  518. X 25, 10, TRUE, m) &&
  519. X        AddText(&gl, 0, ")=", FALSE, expr, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20,
  520. X 160, 10, TRUE, m) &&
  521. X        AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 40
  522. X, 80, 10, TRUE, m) &&
  523. X        AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 40, 8
  524. X0, 10, TRUE, m) &&
  525. X        AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
  526. X 60, 32, 10, TRUE, m) &&
  527. X        AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
  528. X56, 60, 32, 10, TRUE, m) &&
  529. X        (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
  530. X* SELECTED, 0, 9, 80, 10, 10, m)) &&
  531. X        (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
  532. Xedisc * SELECTED, 0, 9, 100, 10, 10, m)) &&
  533. X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 120, 65, 15, FALS
  534. XE, m) &&
  535. X        AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 120, 65, 15
  536. X, FALSE, m))
  537. X    {
  538. X        SetReqGadgets(req, gl);
  539. X        if (ret = DoRequest(req, this->f.o.g, std_ghandler))
  540. X        {
  541. X            *ref = full_refresh(this->f.o.g); /* Redraw everything */
  542. X            /* Extract typed info */
  543. X            this->f.min = str2double(from);
  544. X            this->f.max = str2double(to);
  545. X            this->f.steps = str2int(steps);
  546. X            if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
  547. X1;
  548. X            this->f.showdisc = (sd->Flags & SELECTED) != 0;
  549. X            this->f.nicedisc = (nd->Flags & SELECTED) != 0;
  550. X            strcpy(this->expr, expr);
  551. X            strcpy(this->f.vname, xname);
  552. X
  553. X            /* Calc new dependent info */
  554. X            destroy_f_of_x(this);
  555. X            if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
  556. Xis);
  557. X        }
  558. X    }
  559. X    Free(m);
  560. X
  561. X    return ret;
  562. X}
  563. X
  564. X/* Calculate the points of the function */
  565. Xstatic int calc_f_of_x(struct f_of_x *this, int allow_mes)
  566. X{
  567. X    double x;
  568. X    int i;
  569. X    struct graph *const g = this->f.o.g;
  570. X    /* Use graph limits if none given */
  571. X    double const xmin = this->f.min == NOVAL ? g->a.x.min : this->f.min;
  572. X    double const xmax = this->f.max == NOVAL ? g->a.x.max : this->f.max;
  573. X    int const xlog = g->a.x.log;
  574. X    int const steps = (this->f.steps == INOVAL ? DEFSTEPS : this->f.steps) - 1;
  575. X     
  576. X    double const step = xlog ? pow(xmax / xmin, 1.0 / steps) : (xmax - xmin) /
  577. Xsteps;
  578. X    char func[FNAMELEN + 30];
  579. X
  580. X    new_list(&this->f.pts);
  581. X
  582. X    strcpy(func, "Can't calculate points for ");
  583. X    strcat(func, this->f.o.name);
  584. X    strcat(func, ":");
  585. X
  586. X    if (xmin >= xmax)
  587. X    {
  588. X        if (allow_mes) message(g, func, "xmin >= xmax", (char *)NULL);
  589. X        else alert(g->io.win, "xmin >= xmax", NULL);
  590. X        return FALSE;
  591. X    }
  592. X    if (!create_quick(&this->f.var))
  593. X    {
  594. X        if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
  595. XL);
  596. X        else alert(g->io.win, func, "Couldn't create variable");
  597. X        return FALSE;
  598. X    }
  599. X
  600. X    /* For all steps x values (evenly spaced *on screen*) */
  601. X    for (i = 0, x = xmin; i <= steps; i++, x = xlog ? x * step : x + step)
  602. X    {
  603. X        point *pt = alloc_node(this->f.sizept);
  604. X
  605. X        if (!pt)
  606. X        { /* No mem */
  607. X            free_list(&this->f.pts, this->f.sizept);
  608. X            free_quick(&this->f.var);
  609. X            if (allow_mes) message(g, func, "No memory", (char *)NULL);
  610. X            return FALSE;
  611. X        }
  612. X        add_tail(&this->f.pts, pt);
  613. X
  614. X        pt->x = x;
  615. X        set_quick(&this->f.var, x);
  616. X        pt->y = quick_eval(this->function);
  617. X        pt->state = (eval_error == 0) ? EXISTS : 0;
  618. X    }
  619. X    free_quick(&this->f.var);
  620. X    return TRUE;
  621. X}
  622. X
  623. X/* Draw function */
  624. Xstatic void draw_f_of_x(struct f_of_x *this, int allow_mes)
  625. X{
  626. X    struct graph *g = this->f.o.g;
  627. X
  628. X    /* If xmax or xmin not specified, track values in graph */
  629. X    /* ==> function may need recalculating */
  630. X    if (this->f.calc)
  631. X        if ((this->f.min == NOVAL && this->oldxmin != g->a.x.min) ||
  632. X            (this->f.max == NOVAL && this->oldxmax != g->a.x.max) ||
  633. X            this->waslog != g->a.x.log)
  634. X        {
  635. X            this->f.calc = FALSE;
  636. X            free_list(&this->f.pts, this->f.sizept);
  637. X        }
  638. X
  639. X    if (!this->f.calc) this->f.calc = calc_f_of_x(this, allow_mes);
  640. X
  641. X    if (this->f.calc)
  642. X    {
  643. X        this->oldxmin = g->a.x.min;
  644. X        this->oldxmax = g->a.x.max;
  645. X        this->waslog = g->a.x.log;
  646. X        display_function(&this->f);
  647. X    }
  648. X}
  649. X
  650. X/* Try to improve look of function by adding points. If fails, decides that
  651. X   there is a discontinuity */
  652. Xstatic struct Region *improve_f_of_x(struct f_of_x *this)
  653. X{
  654. X    struct graph *const g = this->f.o.g;
  655. X    point *pt, *next;
  656. X    int ok = FALSE, abort = FALSE, iter;
  657. X    /*what y step constitutes a "flat" segment ? Based on window height, could
  658. Xbe better */
  659. X    double flat = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
  660. X    char msg[FNAMELEN + 30];
  661. X    char pass[20];
  662. X    struct Requester *req;
  663. X    struct Region *full = NULL;
  664. X
  665. X    /* Flat has no meaning when graph incorrect */
  666. X    if (!this->f.o.g->ok) flat = 0.0;
  667. X
  668. X    if (!this->f.calc)
  669. X    {
  670. X        strcpy(msg, this->f.o.name);
  671. X        strcpy(msg, "not calculated!");
  672. X        message(g, msg, (char *)NULL);
  673. X        return NULL;
  674. X    }
  675. X    if (!this->derivee)
  676. X    {
  677. X        strcpy(msg, this->f.o.name);
  678. X        strcat(msg, " wasn't differentiable");
  679. X        message(g, msg, (char *)NULL);
  680. X        return NULL;
  681. X    }
  682. X    if (!create_quick(&this->f.var))
  683. X    {
  684. X        message(g, "Couldn't create variable", (char *)NULL);
  685. X        return NULL;
  686. X    }
  687. X
  688. X    /* Allow user to abort (can take a very long time !) */
  689. X    if (!(req = abort_request(g, "Improve: Pass 1")))
  690. X        message(g, "No Memory !", (char *)NULL);
  691. X    else
  692. X    {
  693. X        /* Whole graph will need redrawing */
  694. X        full = full_refresh(this->f.o.g);
  695. X
  696. X        /* Do MAXITER passes, or until every point ok */
  697. X        for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
  698. X        {
  699. X            sprintf(pass, "Improve: Pass %d", iter);
  700. X            set_abort_msg(req, pass);
  701. X            ok = TRUE; /* True as long as no improvements made this pass */
  702. X
  703. X            /* Scan all but last point */
  704. X            for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
  705. X            {
  706. X                if (aborted(req)) { abort = TRUE; break; }
  707. X
  708. X                if ((pt->state & (EXISTS | OK)) == EXISTS) /* Only exists. Igno
  709. Xre points who's segment is "ok" */
  710. X                {
  711. X                    double dx;
  712. X
  713. X                    pt->state |= OK;
  714. X                    pt->state &= ~DISC;
  715. X
  716. X                    /* Idea: check if the differential at this point provides
  717. X                       a good approximation of the next point. If not, add an
  718. X                       extra one.
  719. X                       Bad idea: Use the differential at the mid point of the
  720. X                       segment. Allows "angles" to remain in the output.
  721. X                       Remark: I've tried various other schemes. This one was
  722. X                       the best.
  723. X                    */
  724. X                    set_quick(&this->f.var, pt->x);
  725. X                    dx = quick_eval(this->derivee);
  726. X                    if (eval_error == 0)
  727. X                    {
  728. X                        double ecart = next->y - pt->y;
  729. X                        /* error: difference between first order taylor approx.
  730. X     
  731. X                           and actual point. */
  732. X                        double error = fabs(ecart - (next->x - pt->x) * dx);
  733. X
  734. X                        /* Should we add a point ? error compared with
  735. X                           difference (on y axis) between the two points, if
  736. X                           nicedisc, small errors (<2 pixels) are accepted
  737. X                           without this check */
  738. X                        if (error > fabs(ecart) * MAXERROR && (!this->f.nicedis
  739. Xc || error > flat))
  740. X                        {
  741. X                            /* Add ONE extra point between the two */
  742. X                            pt->state &= ~OK;
  743. X                            ok = FALSE; /* We've added a point */
  744. X
  745. X                            if (iter == MAXITER) pt->state |= DISC; /* This is
  746. X(maybe) a discontinuity */
  747. X                            else /* currently ignores BREAKUP(Extension: add mo
  748. Xre than one point) */
  749. X                            {
  750. X                                point *newpt = alloc_node(this->f.sizept);
  751. X
  752. X                                if (!newpt)
  753. X                                {
  754. X                                    message(g, "No memory for point !", (char *
  755. X)NULL);
  756. X                                    abort = TRUE;
  757. X                                    break; /* Exit from loop ! */
  758. X                                }
  759. X                                newpt->x = (pt->x + next->x) / 2;
  760. X                                set_quick(&this->f.var, newpt->x);
  761. X                                newpt->y = quick_eval(this->function);
  762. X                                newpt->state = (eval_error == 0) ? EXISTS : 0;
  763. X                                insert(&this->f.pts, newpt, pt);
  764. X                            }
  765. X                        }
  766. X                    }
  767. X                }
  768. X            }
  769. X        }
  770. X        end_abort_request(req);
  771. X    }
  772. X    free_quick(&this->f.var);
  773. X    return full;
  774. X}
  775. X
  776. X/* Provide quick textual form of function */
  777. Xstatic char *f2str_f_of_x(struct f_of_x *this, char *buf, int maxlen)
  778. X{
  779. X    buf[maxlen - 1] = '\0';
  780. X    strncpy(buf, this->f.o.name, maxlen - 1);
  781. X    strncat(buf, "(", maxlen - strlen(buf) - 1);
  782. X    strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
  783. X    strncat(buf, ")=", maxlen - strlen(buf) - 1);
  784. X    strncat(buf, this->expr, maxlen - strlen(buf) - 1);
  785. X
  786. X    return buf;
  787. X}
  788. X
  789. X/* Did user select us ? */
  790. Xstatic int down_f_of_x(struct f_of_x *this)
  791. X{
  792. X    struct graph *g = this->f.o.g;
  793. X
  794. X    if (this->f.o.ok && this->f.calc) /* visible ? */
  795. X    {
  796. X        int inside;
  797. X
  798. X        if (!create_quick(&this->f.var))
  799. X        {
  800. X            message(g, "Couldn't create variable", (char *)NULL);
  801. X            return FALSE;
  802. X        }
  803. X
  804. X        set_quick(&this->f.var, g->s.x);
  805. X        /* Calculate y = f(x click pos) and compare with y click pos */
  806. X        inside = fabs(g->io.rw->sy(g->io.rw, quick_eval(this->function)) - g->i
  807. Xo.rw->sy(g->io.rw, g->s.y)) < FDIST &&
  808. X                 eval_error == 0;
  809. X        free_quick(&this->f.var);
  810. X
  811. X        return inside;
  812. X    }
  813. X    return FALSE;
  814. X}
  815. X
  816. X/* Write f_of_x specific info */
  817. Xstatic int save_f_of_x(struct f_of_x *this, FILE *f)
  818. X{
  819. X    short tag = F_OF_X_TAG;
  820. X    short end = F_OF_X_END;
  821. X
  822. X    return WRITE(f, tag) &&
  823. X           WRITE(f, this->expr) &&
  824. X           WRITE(f, end);
  825. X}
  826. X
  827. X/* Delete a member of class f_of_x */
  828. Xstatic struct Region *delete_f_of_x(struct f_of_x *this)
  829. X{
  830. X    struct Region *full = full_refresh(this->f.o.g);
  831. X
  832. X    destroy_f_of_x(this);
  833. X    FreeMem(this, sizeof(struct f_of_x));
  834. X    return full;
  835. X}
  836. X
  837. X/* Create a new instance of f_of_x */
  838. Xstruct f_of_x *new_f_of_x(struct graph *g, char *name)
  839. X{
  840. X    struct f_of_x *this = AllocMem(sizeof(struct f_of_x), MEMF_CLEAR);
  841. X
  842. X    if (this)
  843. X    {
  844. X        /* Standard init */
  845. X        init_function(&this->f, g, name);
  846. X        /* Setup methods */
  847. X        this->f.save = (void *)save_f_of_x;
  848. X        this->f.o.delete = (void *)delete_f_of_x;
  849. X        this->f.o.down = (void *)down_f_of_x;
  850. X        this->f.o.draw = (void *)draw_f_of_x;
  851. X        this->f.o.edit = (void *)edit_f_of_x;
  852. X        this->f.o.improve = (void *)improve_f_of_x;
  853. X        this->f.o.f2str = (void *)f2str_f_of_x;
  854. X        this->f.sizept = sizeof(point);
  855. X        return this;
  856. X    }
  857. X    message(g, "Couldn't create function:", "No memory", (char *)NULL);
  858. X    return NULL;
  859. X}
  860. X
  861. X/* Load f_of_x from a file */
  862. Xstruct f_of_x *load_f_of_x(struct graph *g, FILE *f)
  863. X{
  864. X    struct f_of_x *this = new_f_of_x(g, "");
  865. X
  866. X    if (this)
  867. X    {
  868. X        short end;
  869. X
  870. X        if (READ(f, this->expr) &&
  871. X            READ(f, end) &&
  872. X            end == F_OF_X_END)
  873. X        {
  874. X            /* Load standard part */
  875. X            load_rest(&this->f, f);
  876. X            if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
  877. Xis);
  878. X
  879. X            return this;
  880. X        }
  881. X        delete_f_of_x(this);
  882. X    }
  883. X    return NULL;
  884. X}
  885. X
  886. X
  887. X
  888. SHAR_EOF
  889. echo "End of archive 1 (of 7)"
  890. # if you want to concatenate archives, remove anything after this line
  891. exit
  892.